﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Xml.Linq;
using XFControls.XFPanels.XFPanelItems;
using XFControls.XFPanels.XFPanelModals;

#pragma warning disable 1591
#pragma warning disable 219
#pragma warning disable 168

// ReSharper disable InconsistentNaming
// ReSharper disable UnaccessedField.Local
// ReSharper disable EnumUnderlyingTypeIsInt
// ReSharper disable PossibleNullReferenceException
// ReSharper disable BitwiseOperatorOnEnumWihtoutFlags
// ReSharper disable EmptyGeneralCatchClause
// ReSharper disable RedundantAssignment

namespace XFControls.Utils
{
    public static class XFGraphics
    {
        internal static string[] IMAGES = new[] { "png", "jpg", "jpeg", "bmp", "gif" };

        public delegate void GetImageDelegate(MemoryStream stream);
        private static Dictionary<string, string> _imageCache;

        private static readonly Dictionary<string, WebDownloaderWrapper> _list = new Dictionary<string, WebDownloaderWrapper>();
        private static string localPath;
        private static string xmlPath;
        private const int maxConnections = 1;
        private static int curImageDownloads;
        private static XDocument imageData;
        private static ScreenResolution _res = ScreenResolution.Unknown;
        private static readonly Object _bltLock = new object();

        private static readonly IImagingFactory factory =
            (IImagingFactory)
            Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("327ABDA8-072B-11D3-9D7B-0000F81EF32E")));

        public static Dictionary<string, string> ImageCache
        {
            get { return _imageCache ?? (_imageCache = new Dictionary<string, string>()); }
        }

        internal static XDocument ImageData
        {
            get
            {
                if (imageData == null)
                {
                    try
                    {
                        if (string.IsNullOrEmpty(localPath))
                            localPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);

                        xmlPath = localPath + @"\XFImageCache.xml";

                        if (File.Exists(xmlPath))
                            imageData = XDocument.Load(xmlPath);
                        else
                            imageData = new XDocument(
                                new XDeclaration("1.0", "utf-8", "yes"),
                                new XElement("imageCacheData"));
                    }
                    catch (Exception)
                    {
                        imageData = new XDocument(
                            new XDeclaration("1.0", "utf-8", "yes"),
                            new XElement("imageCacheData"));
                    }
                }
                return imageData;
            }
        }

        public static ScreenResolution GetScreenResolution()
        {
            if (_res != ScreenResolution.Unknown) return _res;

            Rectangle rec = Screen.PrimaryScreen.Bounds;

            if (rec.Height == 800 && rec.Width == 480)
                return _res = ScreenResolution.WVGA;
            if (rec.Height == 480 && rec.Width == 800)
                return _res = ScreenResolution.RotatedWVGA;
            if (rec.Height == 640 && rec.Width == 480)
                return _res = ScreenResolution.VGA;
            if (rec.Height == 480 && rec.Width == 640)
                return _res = ScreenResolution.RotatedVGA;
            if (rec.Height == 320 && rec.Width == 240)
                return _res = ScreenResolution.QVGA;
            if (rec.Height == 240 && rec.Width == 320)
                return _res = ScreenResolution.RotatedQVGA;

            return ScreenResolution.Unknown;
        }

        public static ScreenResolution GetScreenResolutionNotRotated()
        {
            Rectangle rec = Screen.PrimaryScreen.Bounds;

            if (rec.Height == 800 && rec.Width == 480)
                return ScreenResolution.WVGA;
            if (rec.Height == 480 && rec.Width == 800)
                return ScreenResolution.WVGA;
            if (rec.Height == 640 && rec.Width == 480)
                return ScreenResolution.VGA;
            if (rec.Height == 480 && rec.Width == 640)
                return ScreenResolution.VGA;
            if (rec.Height == 320 && rec.Width == 240)
                return ScreenResolution.QVGA;
            if (rec.Height == 240 && rec.Width == 320)
                return ScreenResolution.QVGA;

            return ScreenResolution.Unknown;
        }

        public static void DoImport(string skinPath)
        {
            if (ImageData.Root == null)
                return;

            foreach (XElement elm in ImageData.Root.Elements("image"))
            {
                var att = elm.Attribute("uri");
                if (att == null)
                    continue;

                string key = att.Value.Decrypt();
                if (!ImageCache.ContainsKey(key))
                    ImageCache.Add(key, elm.Value);
            }

            string[] dirs = Directory.GetDirectories(skinPath);

            foreach (string dir in dirs)
            {
                string[] files = Directory.GetFiles(dir);

                foreach (string file in files)
                {
                    string ext = file.Split('\\').Last().Split('.').First();
                    if (!ImageCache.ContainsKey(ext))
                        ImageCache.Add(ext, file);
                }
            }
            {
                string[] files = Directory.GetFiles(skinPath);

                foreach (string file in files)
                {
                    string ext = file.Split('\\').Last().Split('.').First();
                    if (!ImageCache.ContainsKey(ext))
                        ImageCache.Add(ext, file);
                }
            }
        }

        public static Rectangle GetImageRec(this IXFItem item, string Uri)
        {
            return GetImageRec((object)item, Uri);
        }

        public static Rectangle GetImageRec(this IXFModal item, string Uri)
        {
            return GetImageRec((object)item, Uri);
        }

        public static Rectangle GetImageRec(string Uri)
        {
            using (var item = new XFItemBase())
            {
                return item.GetImageRec(Uri);
            }
        }

        public static Rectangle GetImageRec(object item, string Uri)
        {
            Image i;
            ScreenResolution screen = GetScreenResolutionNotRotated();
            if (ImageCache.ContainsKey(Uri))
            {
                try
                {
                    using (i = XFBitmapFactory.GetBitmap(ImageCache[Uri]))
                    {
                        int width = i.Width;
                        int height = i.Height;

                        if (screen == ScreenResolution.QVGA)
                        {
                            width /= 2;
                            height /= 2;
                        }

                        return new Rectangle(0, 0, width, height);
                    }
                }
                catch (FileNotFoundException)
                {
                    ImageCache.Remove(Uri);
                }
            }

            var strm = GetMemoryAssembly(item, Uri);
            var ret = XFBitmapFactory.GetBitmap(strm);

            if (!(screen == ScreenResolution.VGA || screen == ScreenResolution.WVGA))
            {
                Bitmap temp = ret.Scale(.5);
                ret.Dispose();
                ret = temp;
            }

            if (ret != null)
                using (ret)
                    return new Rectangle(0, 0, ret.Width, ret.Height);

            return new Rectangle();
        }

        public static Bitmap Scale(this Bitmap image, double factor)
        {
            var ret = XFBitmapFactory.GetBitmap((int)(image.Width * factor), (int)(image.Height * factor));
            using (Graphics g = Graphics.FromImage(ret))
            {
                g.DrawImage(image, new Rectangle(0, 0, ret.Width, ret.Height),
                            new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
            }

            return ret;
        }

        /// <summary>
        /// Will create a thumbnail of a picture regardless of the source size.
        /// </summary>
        /// <param name="path">The path to the picture.</param>
        /// <param name="width">The width you want the thumbnail to be.</param>
        /// <param name="height">The height you want the thumbnail to be.</param>
        /// <returns>A thumbnail.</returns>
        public static Bitmap CreateThumbnailFromFile(string path, int width, int height)
        {
            IImage image;
            IImage thumb;

            factory.CreateImageFromFile(path, out image);
            Size s;
            image.GetPhysicalDimension(out s);

            var destination = new Rectangle(0, 0, width, height);
            var imgRec = new Rectangle(0, 0, s.Width, s.Height);
            destination = destination.ScaleToFitInside(imgRec, false);

            var thumbB = XFBitmapFactory.GetBitmap(destination.Width, destination.Height);
            using (Graphics g = Graphics.FromImage(thumbB))
            {
                IntPtr hdc = g.GetHdc();

                var ret = image.GetThumbnail((uint)destination.Width, (uint)destination.Height, out thumb);
                thumb.Draw(hdc, ref destination, (IntPtr)null);
                g.ReleaseHdc(hdc);
            }

            Marshal.ReleaseComObject(image);
            Marshal.ReleaseComObject(thumb);
            return thumbB;
        }

        public static void DrawUIAlphaChannel(this Graphics g, object sender, string Uri, Rectangle recDest)
        {
            drawUIAlphaChannel(sender, g, recDest, Uri);
        }

        public static void DrawUIAlphaChannel(this IXFModal item, string Uri, Graphics g, Rectangle recDest)
        {
            DrawUIAlphaChannel((object)item, Uri, g, recDest);
        }

        public static void DrawUIAlphaChannel(this IXFButton item, string Uri, Graphics g, Rectangle recDest)
        {
            DrawUIAlphaChannel((object)item, Uri, g, recDest);
        }

        public static void DrawUIAlphaChannel(object item, string Uri, Graphics g, Rectangle recDest)
        {
            drawUIAlphaChannel(item, g, recDest, Uri);
        }

        public static void DrawUIFileAlphaChannel(this Graphics g, string file, Rectangle recDest)
        {
            if (!File.Exists(file)) return;

            IImage imagingResource;

            factory.CreateImageFromFile(file, out imagingResource);

            IntPtr hdcDest = g.GetHdc();
            var drawRec = new Rectangle(recDest.X, recDest.Y, recDest.X + recDest.Width, recDest.Y + recDest.Height);
            // Ask the Image object from Imaging to draw itself.

            imagingResource.Draw(hdcDest, ref drawRec, IntPtr.Zero);
            g.ReleaseHdc(hdcDest);
            Marshal.ReleaseComObject(imagingResource);
        }

        public static Bitmap GetBitmap(string file, Rectangle recReturn)
        {
            if (!File.Exists(file)) return null;

            IImage imagingResource;
            var thumbB = XFBitmapFactory.GetBitmap(recReturn.Width, recReturn.Height);

            factory.CreateImageFromFile(file, out imagingResource);

            Size size;
            imagingResource.GetPhysicalDimension(out size);
            var recImg = new Rectangle(0, 0, size.Width, size.Height);

            using (Graphics g = Graphics.FromImage(thumbB))
            {
                bool go = true;

                while (go)
                {
                    IntPtr hdc = g.GetHdc();
                    IntPtr img = Marshal.AllocHGlobal(Marshal.SizeOf(recImg));

                    Marshal.StructureToPtr(recImg, img, false);

                    try
                    {
                        imagingResource.Draw(hdc, ref recReturn, img);
                        go = false;
                    }
                    catch (COMException e)
                    {
                        if (e.ErrorCode == -2147467259)
                            go = false;
                    }
                    catch (Exception)
                    {
                        if (hdc != IntPtr.Zero)
                            g.ReleaseHdc(hdc);

                        hdc = IntPtr.Zero;

                        g.DrawImage(new Bitmap(file), recReturn, recImg, GraphicsUnit.Pixel);
                        break;
                    }
                    finally
                    {
                        try
                        {
                            if (hdc != IntPtr.Zero)
                                g.ReleaseHdc(hdc);

                            Marshal.FreeHGlobal(img);
                            Marshal.ReleaseComObject(imagingResource);
                        }
                        catch (Exception) { }
                    }

                }

            }

            return thumbB;
        }

        public static void RotateImage(int rotationAngle,
                                        Bitmap originalBitmap,
                                        Bitmap rotatedBitmap)
        {
            // It should be faster to access values stored on the stack
            // compared to calling a method (in this case a property) to 
            // retrieve a value. That's why we store the width and height
            // of the bitmaps here so that when we're traversing the pixels
            // we won't have to call more methods than necessary.

            int newWidth = rotatedBitmap.Width;
            int newHeight = rotatedBitmap.Height;

            int originalWidth = originalBitmap.Width;
            int originalHeight = originalBitmap.Height;

            // We're going to use the new width and height minus one a lot so lets 
            // pre-calculate that once to save some more time
            int newWidthMinusOne = newWidth - 1;
            int newHeightMinusOne = newHeight - 1;

            // To grab the raw bitmap data into a BitmapData object we need to
            // "lock" the data (bits that make up the image) into system memory.
            // We lock the source image as ReadOnly and the destination image
            // as WriteOnly and hope that the .NET Framework can perform some
            // sort of optimization based on this.
            // Note that this piece of code relies on the PixelFormat of the 
            // images to be 32 bpp (bits per pixel). We're not interested in 
            // the order of the components (red, green, blue and alpha) as 
            // we're going to copy the entire 32 bits as they are.
            var originalData = originalBitmap.LockBits(
                new Rectangle(0, 0, originalWidth, originalHeight),
                System.Drawing.Imaging.ImageLockMode.ReadOnly,
                PixelFormat.Format32bppRgb);

            var rotatedData = rotatedBitmap.LockBits(
                new Rectangle(0, 0, rotatedBitmap.Width, rotatedBitmap.Height),
                System.Drawing.Imaging.ImageLockMode.WriteOnly,
                PixelFormat.Format32bppRgb);

            // We're not allowed to use pointers in "safe" code so this
            // section has to be marked as "unsafe". Cool!
            unsafe
            {
                // Grab int pointers to the source image data and the 
                // destination image data. We can think of this pointer
                // as a reference to the first pixel on the first row of the 
                // image. It's actually a pointer to the piece of memory 
                // holding the int pixel data and we're going to treat it as
                // an array of one dimension later on to address the pixels.
                var originalPointer = (int*)originalData.Scan0.ToPointer();
                var rotatedPointer = (int*)rotatedData.Scan0.ToPointer();

                // There are nested for-loops in all of these case statements
                // and one might argue that it would have been neater and more
                // tidy to have the switch statement inside the a single nested
                // set of for loops, doing it this way saves us up to three int 
                // to int comparisons per pixel. 
                //

                switch (rotationAngle)
                {
                    case 90:
                        int yOffset = -originalWidth;
                        for (int y = 0; y < originalHeight; ++y)
                        {
                            yOffset += originalWidth;
                            int destinationX = newWidthMinusOne - y;
                            int destinationY = -newWidth;
                            for (int x = 0; x < originalWidth; ++x)
                            {
                                int sourcePosition = (x + yOffset);
                                destinationY += newWidth;
                                int destinationPosition =
                                (destinationX + destinationY);
                                rotatedPointer[destinationPosition] =
                                originalPointer[sourcePosition];
                            }
                        }

                        break;
                    case 180:
                        for (int y = 0; y < originalHeight; ++y)
                        {
                            int destinationY = (newHeightMinusOne - y) * newWidth;
                            for (int x = 0; x < originalWidth; ++x)
                            {
                                int sourcePosition = (x + y * originalWidth);
                                int destinationX = newWidthMinusOne - x;
                                int destinationPosition = (destinationX + destinationY);
                                rotatedPointer[destinationPosition] =
                                    originalPointer[sourcePosition];
                            }
                        }
                        break;
                    case 270:
                        for (int y = 0; y < originalHeight; ++y)
                        {
                            int destinationX = y;
                            for (int x = 0; x < originalWidth; ++x)
                            {
                                int sourcePosition = (x + y * originalWidth);
                                int destinationY = newHeightMinusOne - x;
                                int destinationPosition =
                                    (destinationX + destinationY * newWidth);
                                rotatedPointer[destinationPosition] =
                                    originalPointer[sourcePosition];
                            }
                        }
                        break;
                }

                // We have to remember to unlock the bits when we're done.
                originalBitmap.UnlockBits(originalData);
                rotatedBitmap.UnlockBits(rotatedData);
            }
        }

        public static IImage GetIImage(byte[] pbBuf)
        {
            if (pbBuf == null || pbBuf.Length == 0)
                return null;
            IImage imagingResource;
            IntPtr hdcDest = IntPtr.Zero;

            var cbBuf = (uint)pbBuf.Length;
            Size imageSize;
            bool redo = false;

            factory.CreateImageFromBuffer(pbBuf, cbBuf, BufferDisposalFlag.BufferDisposalFlagNone, out imagingResource);
            imagingResource.SetImageFlags(0x00020000);

            return imagingResource;
        }

        public static void BitBltImage(this Graphics g, Bitmap bitmap, int curY)
        {
            lock (_bltLock)
            {
                using (Graphics gb = Graphics.FromImage(bitmap))
                {
                    var gHDC = g.GetHdc();
                    var bHDC = gb.GetHdc();

                    BitBltImage(gHDC, bHDC, new Rectangle(0, curY, bitmap.Width, bitmap.Height));

                    gb.ReleaseHdc(bHDC);
                    g.ReleaseHdc(gHDC);
                }
            }
        }

        public static void BitBltImage(IntPtr gHDC, IntPtr bHDC, Rectangle rec)
        {
            Win32.BitBlt(gHDC, rec.X, rec.Y, rec.Width, rec.Height, bHDC, 0, 0, Win32.ERop.SRCCOPY);
        }

        //public static bool IsFileLocked(string path)
        //{
        //    if (!File.Exists(path))
        //        return false;

        //    FileStream stream = null;

        //    try
        //    {
        //        stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None);
        //    }
        //    catch (IOException)
        //    {
        //        return true;
        //    }
        //    finally
        //    {
        //        if (stream != null)
        //            stream.Close();
        //    }

        //    return false;
        //}

        public static void ClearDownloadQueue()
        {
            lock (_list)
            {
                foreach (var req in _list)
                    req.Value.Dispose();

                _list.Clear();
            }
        }

        public static void ClearImageCache()
        {
            Cursor.Current = Cursors.WaitCursor;
            Application.DoEvents();

            imageData = null;
            var test = ImageData;

            imageData.Save(xmlPath);
            ImageCache.Clear();

            string imagesPath = localPath + @"\ImageCache";
            string iconPath = localPath + @"\icons";

            if (Directory.Exists(imagesPath))
                Directory.Delete(imagesPath, true);
            if (Directory.Exists(iconPath))
                Directory.Delete(iconPath, true);

            Cursor.Current = Cursors.Default;
            Application.DoEvents();
        }

        private static void drawUIAlphaChannel(object item, Graphics g, Rectangle recDest, string Uri)
        {
            if (ImageCache.Keys.Any(k => k.StartsWith(Uri)))
            {
                try
                {
                    string key = ImageCache.Keys.First(k => k.StartsWith(Uri));
                    DrawUIFileAlphaChannel(g, ImageCache[key], recDest);
                    return;
                }
                catch (FileNotFoundException)
                {
                    ImageCache.Remove(Uri);
                }
                catch (COMException) { }
            }

            using (MemoryStream strm = GetMemoryAssembly(item, Uri))
            {
                if (strm == null)
                    return;

                DrawMemoryStream(g, recDest, strm.GetBuffer(), false);
            }
        }

        public static void DrawMemoryStream(Graphics g, Rectangle recDest, byte[] pbBuf)
        {
            DrawMemoryStream(g, recDest, pbBuf, true);
        }

        public static void DrawMemoryStream(Graphics g, Rectangle recDest, byte[] pbBuf, bool maintainImgRatio)
        {
            DrawMemoryStream(g, recDest, pbBuf, maintainImgRatio, false);
        }

        private static void DrawMemoryStream(Graphics g, Rectangle recDest, byte[] pbBuf, bool maintainImgRatio, bool exception)
        {
            if (pbBuf == null || pbBuf.Length == 0)
                return;

            Size imageSize;
            if (maintainImgRatio)
            {
                imageSize = ImageHeader.GetDimensions(pbBuf);
                double imgRatioWidth = imageSize.Height / (double)imageSize.Width;
                double imgRatioHeight = imageSize.Width / (double)imageSize.Height;
                int x = 0, y = 0;


                if (recDest.Width < imageSize.Width)
                {
                    imageSize.Width = recDest.Width;
                    imageSize.Height = (int)(imgRatioWidth * recDest.Width);

                    recDest.Y = recDest.Height < imageSize.Height ? recDest.Y : ((recDest.Height - imageSize.Height) >> 1) + recDest.Y;
                }
                else if (recDest.Height > imageSize.Height)
                {
                    int destX = recDest.X + ((recDest.Width - imageSize.Width) >> 1);
                    int destY = recDest.Y + ((recDest.Height - imageSize.Height) >> 1);
                    recDest.Height = imageSize.Height;
                    recDest.Width = imageSize.Width;

                    recDest.X = destX;
                    recDest.Y = destY;
                }

                if (recDest.Height < imageSize.Height)
                {
                    imageSize.Height = recDest.Height;
                    imageSize.Width = (int)(imgRatioHeight * recDest.Height);


                    recDest.X = (recDest.Width - imageSize.Width) > 0 ? ((recDest.Width - imageSize.Width) >> 1) + recDest.X : recDest.X;
                }

                recDest.Width = imageSize.Width;
                recDest.Height = imageSize.Height;
            }


            //using (var strm = new MemoryStream(pbBuf))
            //using (var b = XFBitmapFactory.GetBitmap(strm))
            //{
            //    g.DrawImage(b, recDest, new Rectangle(0, 0, b.Width, b.Height), GraphicsUnit.Pixel);
            //    return;
            //}

            bool redo = false;
            IImage imagingResource;
            IntPtr hdcDest = IntPtr.Zero;

            var cbBuf = (uint)pbBuf.Length;

            factory.CreateImageFromBuffer(pbBuf, cbBuf, BufferDisposalFlag.BufferDisposalFlagNone, out imagingResource);

            try
            {
                var drawRec = new Rectangle(recDest.X, recDest.Y, recDest.X + recDest.Width, recDest.Y + recDest.Height);

                lock (g)
                {
                    hdcDest = g.GetHdc();
                    // Ask the Image object from Imaging to draw itself.
                    imagingResource.Draw(hdcDest, ref drawRec, IntPtr.Zero);
                }
            }
            catch (COMException e)
            {
                if (e.ErrorCode == -2147467259 && !exception)
                    redo = true;
            }
            catch (ObjectDisposedException) { }
            catch (ArgumentException) { }
            finally
            {
                try
                {
                    if (hdcDest != IntPtr.Zero)
                        g.ReleaseHdc(hdcDest);

                    Marshal.ReleaseComObject(imagingResource);
                }
                catch (Exception) { }
            }

            if (redo)
                DrawMemoryStream(g, recDest, pbBuf, maintainImgRatio, true);
        }

        public static void DrawMemoryStream(Graphics g, int x, int y, byte[] pbBuf)
        {
            IImage imagingResource;
            IntPtr hdcDest = IntPtr.Zero;
            var cbBuf = (uint)pbBuf.Length;
            Size imageSize;

            factory.CreateImageFromBuffer(pbBuf, cbBuf, BufferDisposalFlag.BufferDisposalFlagNone,
                                          out imagingResource);
            try
            {
                imageSize = ImageHeader.GetDimensions(pbBuf);
                hdcDest = g.GetHdc();
                var drawRec = new Rectangle(x, y, x + imageSize.Width, y + imageSize.Height);
                // Ask the Image object from Imaging to draw itself.
                imagingResource.Draw(hdcDest, ref drawRec, IntPtr.Zero);
            }
            catch (COMException) { }
            catch (ObjectDisposedException) { }
            catch (ArgumentException) { }
            finally
            {
                if (hdcDest != IntPtr.Zero)
                    g.ReleaseHdc(hdcDest);

                Marshal.ReleaseComObject(imagingResource);
            }
        }

        public static void AddToList(IXFItem item, string Uri, GetImageDelegate callback)
        {
            lock (_list)
            {
                if (_list.ContainsKey(Uri))
                {
                    _list[Uri].GetImageCallbacks.Add(callback);
                    return;
                }

                var wpr = new WebDownloaderWrapper(Uri) { Tag = item };
                wpr.GetImageCallbacks.Add(callback);

                if (curImageDownloads <= maxConnections)
                {
                    curImageDownloads++;
                    wpr.BeginDownloadFile();
                }

                wpr.DownloadFileComplete += DownloadFileComplete;

                _list.Add(Uri, wpr);
            }
        }

        private static void DownloadFileComplete(object sender, EventArgs e)
        {
            curImageDownloads--;
            if (curImageDownloads < 0)
                curImageDownloads = 0;

            var wrapper = (WebDownloaderWrapper)sender;
            lock (_list)
            {
                _list.Remove(wrapper.Url);

                foreach (var wpr in _list.Values)
                {
                    if (curImageDownloads > maxConnections || wpr.IsDownloading) continue;

                    curImageDownloads++;
                    wpr.BeginDownloadFile();
                }

                if (!(wrapper.Tag is IXFItem)) return;

                var itm = (IXFItem)wrapper.Tag;
                if (itm.Parent == null) return;
                itm.Parent.Damage();
            }
        }

        public static MemoryStream GetMemoryAssembly(object item, string Uri)
        {
            Type t = item.GetType();
            try
            {
                string asmb = getResourceName(item, Uri);

                if (string.IsNullOrEmpty(asmb))
                    return null;

                MemoryStream myStream;

                while (true)
                {
                    Assembly myAssembly = t.Assembly;
                    myStream = (MemoryStream)myAssembly.GetManifestResourceStream(asmb);

                    if (myStream != null || t.BaseType == typeof(object))
                        break;

                    t = t.BaseType;
                }
                return myStream;
            }
            catch (Exception)
            {
            }

            return null;
        }

        private static string getResourceName(object item, string Uri)
        {
            ScreenResolution screenNotRotated = GetScreenResolutionNotRotated();

            Type t = item.GetType();

            string ret = "";
            while (string.IsNullOrEmpty(ret))
            {
                Assembly myAssembly = t.Assembly;
                string ns = myAssembly.GetName().Name;

                string uriScreen = String.Join(String.Empty, new[] { Uri, "_", screenNotRotated.ToString() });
                string[] resources = myAssembly.GetManifestResourceNames();

                //Array.Sort(resources);
                ret = findResource(resources, ns, uriScreen, Uri);

                if (!string.IsNullOrEmpty(ret) || t.BaseType == typeof(object))
                    break;

                t = t.BaseType;
            }

            return ret;
        }

        private static string findResource(string[] resources, string ns, string uriScreen, string Uri)
        {
            //string usDot = String.Join(String.Empty, new[] { ns, ".Resources.", uriScreen, "." });
            //string usUnder = String.Join(String.Empty, new[] { ns, ".Resources.", uriScreen, "_" });
            //string uDot = String.Join(String.Empty, new[] { ns, ".Resources.", Uri, "." });
            //string uUnder = String.Join(String.Empty, new[] { ns, ".Resources.", Uri, "_" });

            string usDot = ns + ".Resources." + uriScreen + ".";
            string usUnder = ns + ".Resources." + uriScreen + "_";
            string uDot = ns + ".Resources." + Uri + ".";
            string uUnder = ns + ".Resources." + Uri + "_";


            foreach (string s in resources)
            {
                if (s.StartsWith(usDot) || s.StartsWith(usUnder) || s.StartsWith(uDot) || s.StartsWith(uUnder))
                    return s;
            }

            return String.Empty;
        }

        public static void CopyStream(Stream input, Stream output)
        {
            var buffer = new byte[8 * 1024];
            int len;

            while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, len);
            }
        }

        public static bool IsCached(string uri)
        {
            return ImageData.Root.Elements().Any(e => e.Name == "image" && e.Attribute("uri").Value == uri.Encrypt());
        }

        public static void CacheImage(string uri, byte[] array)
        {
            string uriEnc = uri.Encrypt();
            XElement elm = null;

            string dir = XFControlUtils.GetCurrent_Directory() + "\\ImageCache";
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);

            lock (ImageData)
            {
                if ((elm = ImageData.Root.Elements("image").FirstOrDefault(e => e.Attribute("uri").Value == uriEnc)) != null)
                {
                    DateTime time = DateTime.Now;

                    try
                    {
                        time = DateTime.Parse(elm.Attribute("updateTime").Value);

                        if (time.AddHours(5) > DateTime.Now && File.Exists(elm.Value))
                            return;
                    }
                    catch { }

                }

                if (elm != null)
                {
                    elm.Attribute("updateTime").SetValue(DateTime.Now.ToString());
                    try
                    {
                        ImageData.Save(xmlPath);
                    }
                    catch (ArgumentException) { }

                    var loc = elm.Value;
                    using (var str = File.Open(loc, FileMode.OpenOrCreate))
                    using (var binWriter = new BinaryWriter(str))
                    {
                        binWriter.Write(array);
                        return;
                    }
                }


                Guid g = Guid.NewGuid();

                string ext = uri.Split('.').Last();
                string fileLoc = dir + "\\" + g;

                if (IsImage(uri))
                    fileLoc += "." + ext;
                else
                    fileLoc += ".png";

                lock (typeof(XFGraphics))
                {
                    using (var str = File.Open(fileLoc, FileMode.OpenOrCreate))
                    using (var binWriter = new BinaryWriter(str))
                    {
                        binWriter.Write(array);
                    }
                }
                elm = new XElement("image", new XAttribute("uri", uriEnc), new XAttribute("updateTime", DateTime.Now.ToString()), fileLoc);
                ImageData.Root.Add(elm);
                try
                {
                    ImageData.Save(xmlPath);
                }
                catch (ArgumentException) { }

                ImageCache.Add(uri, elm.Value);
            }
        }

        public static bool IsImage(string fileName)
        {
            return IMAGES.Contains(fileName.ToLower().Split('.').Last());
        }
    }
}


/////////// Trash //////////////


//private static Bitmap cacheImage(string uri, MemoryStream stream)
//{
//    if (stream == null)
//        return null;

//    string path = localPath + @"\images\";
//    if (!Directory.Exists(path))
//        Directory.CreateDirectory(path);

//    if (!isCached(uri))
//    {
//        Guid g = Guid.NewGuid();
//        path += g + ".png";

//        lock (imageData)
//        {
//            if (!ImageCache.ContainsKey(uri))
//            {
//                imageData.Root.Add(new XElement("image", new XAttribute("uri", uri.Encrypt()),
//                                                new XAttribute("guid", g.ToString()),
//                                                new XAttribute("type", "image"), path));
//                ImageCache.Add(uri, path);
//            }
//        }
//    }
//    else
//    {
//        XElement elm =
//            imageData.Root.Elements().First(e => e.Name == "image" && e.Attribute("uri").Value == uri.Encrypt());
//        path += elm.Attribute("guid").Value + ".png";
//    }

//    using (FileStream fStream = File.Create(path))
//        copyStream(stream, fStream);

//    Bitmap b = null;

//    try
//    {
//        b = new Bitmap(path);
//    }
//    catch
//    {
//    }
//    return b;
//}

//private static Bitmap cacheIcon(string uri, MemoryStream stream)
//{
//    if (stream == null)
//        return null;

//    string path = localPath + @"\icons\";
//    if (!Directory.Exists(path))
//        Directory.CreateDirectory(path);

//    if (!isCached(uri))
//    {
//        Guid g = Guid.NewGuid();
//        path += g + ".png";

//        lock (imageData)
//        {
//            if (!ImageCache.ContainsKey(uri))
//            {
//                imageData.Root.Add(new XElement("image", new XAttribute("uri", uri.Encrypt()),
//                                                new XAttribute("guid", g.ToString()),
//                                                new XAttribute("type", "icon"), path));
//                ImageCache.Add(uri, path);
//            }
//        }
//    }
//    else
//    {
//        XElement elm =
//            imageData.Root.Elements().First(e => e.Name == "image" && e.Attribute("uri").Value == uri.Encrypt());
//        path += elm.Attribute("guid").Value + ".png";
//    }

//    using (FileStream fStream = File.Create(path))
//        copyStream(stream, fStream);

//    Bitmap b = null;

//    try
//    {
//        b = new Bitmap(path);
//    }
//    catch { }
//    return b;
//}


//public static void DrawIcon(this IXFItem item, string Uri, Graphics g, Rectangle recDest)
//{
//    if (ImageCache.ContainsKey(Uri))
//    {
//        try
//        {
//            using (Image i = new Bitmap(ImageCache[Uri]))
//            {
//                var recSrc = new Rectangle(0, 0, i.Width, i.Height);
//                if (recDest.Width == 0 || recDest.Height == 0)
//                    recDest = new Rectangle(recDest.X, recDest.Y, i.Width, i.Height);

//                g.DrawImage(i, recDest, recSrc, GraphicsUnit.Pixel);
//                if (!item.Parent.IsDisposed && item.Parent.InvokeRequired)
//                    item.Parent.Invoke(new Action(() => item.Parent.Invalidate()));
//                return;
//            }
//        }
//        catch (ArgumentException)
//        {
//            ClearImageCache(Uri);
//        }
//        catch (FileNotFoundException)
//        {
//            ClearImageCache(Uri);
//        }
//        catch (Exception)
//        {
//        }
//    }

//    DrawUI(item, "iconThumb", g, recDest);
//    AddToList(item, Uri, stream =>
//                         {
//                             Bitmap i = cacheIcon(Uri, stream);
//                             if (i == null)
//                                 return;

//                             var recSrc = new Rectangle(0, 0, i.Width, i.Height);
//                             if (recDest.Width == 0 || recDest.Height == 0)
//                                 recDest = new Rectangle(recDest.X, recDest.Y, i.Width, i.Height);

//                             if (item.Buffer != null)
//                                 using (Graphics gi = Graphics.FromImage(item.Buffer))
//                                     gi.DrawImage(i, recDest, recSrc, GraphicsUnit.Pixel);
//                         });
//}

//public static void DrawImage(this IXFItem item, string Uri, Graphics g, Rectangle recDest,
//                             bool maintainAspectRatio)
//{
//    if (ImageCache.ContainsKey(Uri))
//    {
//        try
//        {
//            using (Image i = new Bitmap(ImageCache[Uri]))
//            {
//                var recSrc = new Rectangle(0, 0, i.Width, i.Height);
//                drawImageAspect(g, recDest, recSrc, i, maintainAspectRatio);
//                return;
//            }
//        }
//        catch (ArgumentException)
//        {
//            ClearImageCache(Uri);
//        }
//        catch (FileNotFoundException)
//        {
//            ClearImageCache(Uri);
//        }
//        catch (Exception)
//        {
//        }
//    }

//    DrawUI(item, "image", g, recDest);
//    AddToList(item, Uri, stream =>
//                         {
//                             Bitmap i = cacheImage(Uri, stream);
//                             if (i == null)
//                                 return;

//                             var recSrc = new Rectangle(0, 0, i.Width, i.Height);
//                             if (item.Buffer != null)
//                                 using (Graphics gi = Graphics.FromImage(item.Buffer))
//                                 {
//                                     using (Brush br = new SolidBrush(item.Style.BackgroundColor))
//                                         gi.FillRectangle(br, recDest);
//                                     drawImageAspect(gi, recDest, recSrc, i, maintainAspectRatio);
//                                     if (!item.Parent.IsDisposed && item.Parent.InvokeRequired)
//                                         item.Parent.Invoke(new Action(() => item.Parent.Invalidate()));
//                                 }
//                         });
//}

//public static void DrawUI(this IXFItem item, string Uri, Graphics g, Rectangle recDest)
//{
//    drawUI(item, Uri, g, recDest);
//}
//private static void drawImageAspect(Graphics g, Rectangle recDest, Rectangle recSrc, Image i,
//                                    bool maintainAspectRatio)
//{
//    if (!maintainAspectRatio)
//        g.DrawImage(i, recDest, recSrc, GraphicsUnit.Pixel);
//    else
//    {
//        if (recSrc.Width - recDest.Width > recSrc.Height - recDest.Height)
//        {
//            double ratio = recSrc.Height / (double)recSrc.Width;
//            var newHeight = (int)(recDest.Height * ratio);
//            int newY = recDest.Y + ((recDest.Height - newHeight) / 2);
//            recDest = new Rectangle(recDest.X, newY, recDest.Width, newHeight);
//        }
//        else
//        {
//            double ratio = recSrc.Width / (double)recSrc.Height;
//            var newWidth = (int)(recDest.Width * ratio);
//            int newX = recDest.X + ((recDest.Width - newWidth) / 2);
//            recDest = new Rectangle(newX, recDest.Y, newWidth, recDest.Height);
//        }
//        g.DrawImage(i, recDest, recSrc, GraphicsUnit.Pixel);
//    }
//}

//private static void startImageDownloading()
//{
//    ThreadStart start = () =>
//    {

//        while (_list.Count > 0) // _list contains all of the images that need to be downloaded
//        {
//            ImageRequest req; // ImageRequests are containers that hold all the request info
//            lock (_list)
//            {
//                if (_list.Count > 0)
//                    req = _list.Dequeue();
//                else continue;
//            }

//            if (req.Item.Parent.IsDisposed)
//                continue;

//            using (MemoryStream i = getImageWeb(req))
//                if (i != null)
//                {
//                    foreach (var callback in req.Callbacks)
//                        callback(i);    //  Callbacks is a List<ImageDelegate> object

//                    if (req.Item.Parent != null && !req.Item.Parent.IsDisposed)
//                        req.Item.Parent.Damage();   //  Cross thread Invalidate call
//                }
//        }

//        Thread.CurrentThread.Priority = ThreadPriority.Lowest;
//    };

//    Thread t = new Thread(start) { IsBackground = true, Priority = ThreadPriority.BelowNormal };

//    t.Start();

//    _downloadThreads.Add(t);
//}